package com.nulldev.util.data.QueuedArrays;

import java.io.Serializable;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;

import com.nulldev.util.concurrency.threadIt.v4.emapi.ExecutorManager;
import com.nulldev.util.concurrency.threadIt.v4.emapi.ft.FutureTask;
import com.nulldev.util.concurrency.threadIt.v4.emapi.uft.Executable;
import com.nulldev.util.data.Variables;

public class QueuedMap<K, V> implements Map<K, V>, Serializable {

	private static final long serialVersionUID = -3826268630482430970L;
	private final BlockingDeque<Executable<?>> tasks = new LinkedBlockingDeque<Executable<?>>();
	private final Map<K, V> core;
	private volatile boolean stop;
	private final FutureTask<?> o = new FutureTask<Object>() {

		@Override
		public Object execute() {
			while (QueuedMap.this.core != null || QueuedMap.this.stop) {
				try {
					final Executable<?> o = QueuedMap.this.tasks.take();
					o.run_st();
				} catch (Exception e) {
				}
			}
			return null;
		}
	};

	public QueuedMap(final Map<K, V> map) {
		Variables.notNullE(map);
		this.core = map;
		ExecutorManager.get().queueTask(this.o);
	}

	public static <K, V> QueuedMap<K, V> make() {
		return new QueuedMap<K, V>(new ConcurrentHashMap<>());
	}

	public static <K, V> QueuedMap<K, V> make(final int size) {
		return new QueuedMap<K, V>(new ConcurrentHashMap<>(size));
	}

	private <E> Executable<E> addTask(final Executable<E> t, final QueuedOperationType op) {
		try {
			if (op == QueuedOperationType.WRITE || op == QueuedOperationType.FINALIZER) {
				tasks.offerFirst(t, 300, TimeUnit.MILLISECONDS);
			} else {
				tasks.offerLast(t, 300, TimeUnit.MILLISECONDS);
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return t;
	}

	@Override
	public int size() {
		return this.addTask(new FutureTask<Integer>() {

			@Override
			public Integer execute() {
				return QueuedMap.this.core.size();
			}

		}, QueuedOperationType.READ).waitTillCompletion();
	}

	@Override
	public boolean isEmpty() {
		return this.addTask(new FutureTask<Boolean>() {

			@Override
			public Boolean execute() {
				return QueuedMap.this.core.isEmpty();
			}

		}, QueuedOperationType.READ).waitTillCompletion();
	}

	@Override
	public boolean containsKey(final Object key) {
		return this.addTask(new FutureTask<Boolean>() {

			@Override
			public Boolean execute() {
				return QueuedMap.this.core.containsKey(key);
			}

		}, QueuedOperationType.READ).waitTillCompletion();
	}

	@Override
	public boolean containsValue(final Object value) {
		return this.addTask(new FutureTask<Boolean>() {

			@Override
			public Boolean execute() {
				return QueuedMap.this.core.containsValue(value);
			}

		}, QueuedOperationType.READ).waitTillCompletion();
	}

	@Override
	public V get(final Object key) {
		return this.addTask(new FutureTask<V>() {

			@Override
			public V execute() {
				return QueuedMap.this.core.get(key);
			}

		}, QueuedOperationType.READ).waitTillCompletion();
	}

	@Override
	public V put(final K key, final V value) {
		return this.addTask(new FutureTask<V>() {

			@Override
			public V execute() {
				return QueuedMap.this.core.put(key, value);
			}

		}, QueuedOperationType.WRITE).waitTillCompletion();
	}

	@Override
	public V remove(final Object key) {
		return this.addTask(new FutureTask<V>() {

			@Override
			public V execute() {
				return QueuedMap.this.core.remove(key);
			}

		}, QueuedOperationType.WRITE).waitTillCompletion();
	}

	@Override
	public void putAll(final Map<? extends K, ? extends V> m) {
		this.addTask(new FutureTask<Object>() {

			@Override
			public Object execute() {
				QueuedMap.this.core.putAll(m);
				return null;
			}

		}, QueuedOperationType.WRITE).waitTillCompletion();
	}

	@Override
	public void clear() {
		this.addTask(new FutureTask<Object>() {

			@Override
			public Object execute() {
				QueuedMap.this.core.clear();
				return null;
			}

		}, QueuedOperationType.FINALIZER).waitTillCompletion();
	}

	@Override
	public Set<K> keySet() {
		return this.addTask(new FutureTask<Set<K>>() {

			@Override
			public Set<K> execute() {
				return QueuedMap.this.core.keySet();
			}

		}, QueuedOperationType.READ).waitTillCompletion();
	}

	@Override
	public Collection<V> values() {
		return this.addTask(new FutureTask<Collection<V>>() {

			@Override
			public Collection<V> execute() {
				return QueuedMap.this.core.values();
			}

		}, QueuedOperationType.READ).waitTillCompletion();
	}

	@Override
	public Set<Entry<K, V>> entrySet() {
		return this.addTask(new FutureTask<Set<Entry<K, V>>>() {

			@Override
			public Set<Entry<K, V>> execute() {
				return QueuedMap.this.core.entrySet();
			}

		}, QueuedOperationType.READ).waitTillCompletion();
	}

	public static <K, V> Map<K, V> createTree() {
		return new QueuedMap<K, V>(new TreeMap<K, V>());
	}

	/* NCOMPAT: finalize() was removed as JRE 18 removes finalization. */

}
